// Object.cpp //
#include "Object.h"
#include "Game.h"
#include "Player.h"
#include "Enemy.h"

//Low level class, basically used by anything that will display a graphic
// and will move around, from stars to the player. Has default render control
//collision control, etc

vector<Object*> Object::ObjectList;
vector<Object*> Object::ObjectAddList;

Object::Object(string keyname) // constructor
{
	sprite = new Sprite(keyname);
	position = Vector(0, 0); // everything has a position
	direction = Vector(0, 0); // a direction of movement
	speed = 0.0f;	// a speed of movement
	dead = false; // and most can be killed(need to be removed)

    ObjectAddList.push_back(this);
}

Object::~Object()
{
	delete sprite;
}

void Object::UpdateObjects(float deltaTime) // updates every object
{
	for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); ++Iter)
	   (*Iter)->Update(deltaTime);

	AddNew();
	RemoveDead();
}

//default update, most classes will override this, but will also call back to this
//in their own update function
void Object::Update(float deltaTime)
{
	sprite->Update(deltaTime);
	direction.Normalize();
	Move(direction * speed * deltaTime);
}


//renders all objects based on layer number, this ensures that background renders under
//enemies, ships, etc
void Object::RenderObjects(SDL_Surface* screen) 
{
	for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); ++Iter)
		if((*Iter)->sprite->texture->layer==1)
	   (*Iter)->Render(screen);
	for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); ++Iter)
		if((*Iter)->sprite->texture->layer==2)
	   (*Iter)->Render(screen);
	for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); ++Iter)
		if((*Iter)->sprite->texture->layer==3)
	   (*Iter)->Render(screen);
	for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); ++Iter)
		if((*Iter)->sprite->texture->layer==4)
	   (*Iter)->Render(screen);
	for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); ++Iter)
		if((*Iter)->sprite->texture->layer==5)
	   (*Iter)->Render(screen);

}

//basic render, overridden by anything that displays its own number
void Object::Render(SDL_Surface* screen)
{
	sprite->Render(screen, position);
}

// temporarily hold new objects
void Object::AddNew()
{
    for(vector<Object*>::iterator Iter = ObjectAddList.begin(); Iter != ObjectAddList.end(); ++Iter)
	   ObjectList.push_back((*Iter));

	ObjectAddList.clear();
}

//remove objects that are no longer needed
void Object::RemoveDead()
{
	PlayerShip::RemoveKilled();
	Enemy::RemoveKilled();
	
    for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); Iter += 0)
        if((*Iter)->dead)
        {
			Object* temp = (*Iter);
	    	ObjectList.erase(Iter);
	    	delete temp;
		}
		else
		    ++Iter;
}

//remove all objects
void Object::RemoveAll()
{
	PlayerShip::RemoveAll();
	Enemy::RemoveAll();
	
    for(vector<Object*>::iterator Iter = ObjectList.begin(); Iter != ObjectList.end(); Iter += 0)
    {
		Object* temp = (*Iter);
	    ObjectList.erase(Iter);
	    delete temp;
	}

	ObjectList.clear();
}

void Object::Remove()
{
	dead = true;
}

void Object::Move(Vector amount)
{
	position += amount;
}
// finds the bounding box around an object
SDL_Rect Object::GetBounds() 
{
	SDL_Rect bounds;
	bounds.x = (Sint16)(position.X - sprite->origin.X);
	bounds.y = (Sint16)(position.Y - sprite->origin.Y);
	bounds.w = (Sint16)(sprite->texture->tileWidth);
	bounds.h = (Sint16)(sprite->texture->tileWidth);

	return bounds;
}

//creates easier to use rectangle
SDL_Rect Object::NormalizeBounds(const SDL_Rect& rect)
{
	SDL_Rect normalizeRect;
	normalizeRect.x = (Sint16)(rect.x + sprite->GetFrameBounds().x + sprite->origin.X - position.X);
	normalizeRect.y = (Sint16)(rect.y + sprite->GetFrameBounds().y + sprite->origin.Y - position.Y);
	normalizeRect.w = rect.w;
	normalizeRect.h = rect.h;

	return normalizeRect;
}

//does first step of collision detection, returns a rectangle consisting of the 
//intersecting area between two bounding boxes
SDL_Rect Object::Intersection(const SDL_Rect& boundsA, const SDL_Rect& boundsB)
{
	int x1 = Maximum(boundsA.x, boundsB.x);
	int y1 = Maximum(boundsA.y, boundsB.y);
	int x2 = Minimum(boundsA.x + boundsA.w, boundsB.x + boundsB.w);
	int y2 = Minimum(boundsA.y + boundsA.h, boundsB.y + boundsB.h);
	
	int width = x2 - x1;
	int height = y2 - y1;
	
	if(width > 0 && height > 0)
	{
		SDL_Rect intersect = {x1, y1, width, height};
		return intersect;
	}
	else
	{
		SDL_Rect intersect = {0, 0, 0, 0};
		return intersect;
	}
}

//does collision detection, will find intersectimg rectangle, and if area is non
// zero, it will check to see if there are any intersecting opaque pixels within
// that area, to allow for much better collision accuracy
bool Object::CheckCollision(Object* objectA, Object* objectB)
{
	SDL_Rect collisionRect = Intersection(objectA->GetBounds(), objectB->GetBounds());
	
	if(collisionRect.w == 0 && collisionRect.h == 0)
	    return false;
	    
	SDL_Rect normalA = objectA->NormalizeBounds(collisionRect);
	SDL_Rect normalB = objectB->NormalizeBounds(collisionRect);
	
	SDL_Surface* surfaceA = objectA->sprite->texture->textures[0];
	SDL_Surface* surfaceB = objectB->sprite->texture->textures[0];
	
	for(int y = 0; y <= collisionRect.h; ++y)
	    for(int x = 0; x <= collisionRect.w; ++x)
			if(GetAlphaXY(surfaceA, normalA.x + x, normalA.y + y) && GetAlphaXY(surfaceB, normalB.x + x, normalB.y + y))
			{
				if(objectA->numValue || objectB->numValue)
				{
					objectB->numValue += objectA->numValue;
					if(objectB->numValue%divide == 0)
						objectB->numValue = -1;
				}
				return true;
			}

	return false;
}

// check for opaque pixel intersection, used above for collsion check
bool Object::GetAlphaXY(SDL_Surface* surface, int x, int y)
{
	int bpp = surface->format->BytesPerPixel;
	Uint8* p = (Uint8*)surface->pixels + y * surface->pitch + x * bpp;
	Uint32 pixelColor;

	switch(bpp)
	{
		case(1):
			pixelColor = *p;
			break;
		case(2):
			pixelColor = *(Uint16*)p;
			break;
		case(3):
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
				pixelColor = p[0] << 16 | p[1] << 8 | p[2];
			else
				pixelColor = p[0] | p[1] << 8 | p[2] << 16;
			break;
		case(4):
			pixelColor = *(Uint32*)p;
			break;
	}

	Uint8 red, green, blue, alpha;
	SDL_GetRGBA(pixelColor, surface->format, &red, &green, &blue, &alpha);

	return alpha == 255;
}

